package main

import (
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"runtime"
	"strconv"
	"sync"
	"time"
)

/**
http pprof:
通过导入net/http/pprof，开启pprof数据采样
http://ip:port/debug/pprof/CMD  通过此命令获取采样数据。CMD有：

goroutine  获取当前所有goroutine堆栈信息 (goroutine dump)
heap  每个goroutine分配大小、分配堆栈等。每分配runtime.MemProfileRate个字节进行一次数据采样
threadcreate  获取导致创建OS线程的goroutine堆栈
block  获取导致阻塞的goroutine堆栈（如channel, mutex等），使用前先调用runtime.SetBlockProfileRate()
mutex  获取导致mutex争用的goroutine堆栈，使用前先调用runtime.SetMutexProfileFraction()

cmdline  获取命令行启动参数
profile  获取指定时间内的cpu profile。参数seconds, 默认30s。cpuprofile 每秒钟采样100次，收集当前运行的 goroutine 堆栈信息
trace  对应用程序执行追踪，参数seconds，默认1s

cpu占用分析：
(1) 运行go tool pprof http://localhost:8000/debug/pprof/profile命令， 该命令默认采样30s，生成pprof文件并进入go tool pprof分析客户端
(2) 分析命令：
topN  占用cpu最多的前N个方法  分析指标： flat  flat%  sum%  cum  cum% （flat为本方法占用时间，不包含子方法; cum为包含子方法的占用时间）
list funcName  分析funcName方法的每行代码占用时间
web  funcName  funcName方法占用时间生成svg图，浏览器端查看
(3) 可以在浏览器端点击/debug/pprof/profile命令，默认采样30s后下载pprof文件, 使用go tool pprof分析

内存占用分析：
(1) 运行go tool pprof http://localhost:8000/debug/pprof/heap命令， 此命令dump当前堆内存，并进入go tool pprof客户端
(2) 分析命令：
topN  内存分配最多的前N个方法  分析指标： flat  flat%  sum%  cum  cum% （flat为本方法分配内存，不包含子方法; cum为包含子方法的分配内存）
list funcName  分析funcName方法的每行代码内存分配
web  funcName  funcName方法内存分配生成svg图，浏览器端查看
(3) /debug/pprof/heap选项：--inuse_space(默认), --inuse_objects, --alloc_space, --alloc_objects

block和mutex分析：
go tool pprof http://localhost:8000/debug/pprof/block
go tool pprof http://localhost:8000/debug/pprof/mutex
分析命令：top  list web
block为goroutine阻塞， mutex为互斥锁争用

goroutine分析：
go tool pprof http://localhost:8000/debug/pprof/goroutine
分析命令：top  list web
top: 分析当前goroutine堆栈正在执行最多的N个方法

trace分析：
（1）浏览器端点击/debug/pprof/trace，生成trace文件并下载
（2）go tool trace traceFile分析文件，默认在浏览器端打开web页面
（3）trace分析：goroutine analysis, network blocking, synchronization blocking, syscall blocking等
*/
func Server1() {
	http.HandleFunc("/get1", func(writer http.ResponseWriter, request *http.Request) {
		writer.Write([]byte("get1"))

		s := make([]int, 1024)
		fmt.Println(s)
		writer.WriteHeader(200)
	})

	http.HandleFunc("/get2", func(writer http.ResponseWriter, request *http.Request) {
		writer.Write([]byte("get2"))

		Cost1()
		writer.WriteHeader(200)
	})

	http.HandleFunc("/get3", func(writer http.ResponseWriter, request *http.Request) {
		writer.Write([]byte("get3"))

		Cost2()
		s := make([]int, 1600)
		fmt.Println(s)
		writer.WriteHeader(200)
	})

	http.HandleFunc("/get4", func(writer http.ResponseWriter, request *http.Request) {
		writer.Write([]byte("get4"))
		writer.WriteHeader(200)
	})

	http.HandleFunc("/block", func(writer http.ResponseWriter, request *http.Request) {
		i := TestBlock()
		writer.Write([]byte(strconv.Itoa(i)))
		writer.WriteHeader(200)
	})

	http.HandleFunc("/block2", func(writer http.ResponseWriter, request *http.Request) {
		time.Sleep(200 * time.Millisecond)
		writer.Write([]byte("aa"))
		writer.WriteHeader(200)
	})

	http.HandleFunc("/mutex", func(writer http.ResponseWriter, request *http.Request) {
		sum := TestMutex()
		writer.Write([]byte(strconv.Itoa(sum)))
		writer.WriteHeader(200)
	})

	// rate = 1 时, 统计所有的 block event,
	// rate <=0 时，则关闭block profiling
	// rate > 1 时，为 ns 数，阻塞时间t>rate的event 一定会被统计，小于rate则有t/rate 的几率被统计
	runtime.SetBlockProfileRate(1 * 1000 * 1000)

	// 当 rate = 0 时，关闭 mutex prof (默认值)
	// 当 rate = 1 时，表示记录所有的 mutex event
	// 当 rate > 1 时，记录 1/rate 的 mutex event(随机)
	runtime.SetMutexProfileFraction(1)
	http.ListenAndServe(":8000", nil)
}

func Cost1() {
	sum := 0
	for i := 0; i < (1 << 31); i++ {
		sum += i
	}

	fmt.Println(sum)
}

func Cost2() {
	sum := 0
	for i := 0; i < (1 << 32); i++ {
		sum += i
	}

	fmt.Println(sum)
}

func TestBlock() int {
	ch := make(chan int)
	go func() {
		time.Sleep(500 * time.Millisecond)
		ch <- 100
	}()

	i := <-ch
	return i
}

func TestMutex() int {
	lock := sync.Mutex{}

	i := 0
	sum := 0

	wg := sync.WaitGroup{}
	wg.Add(100)
	for j := 0; j < 100; j++ {
		go func(locker sync.Locker, wg *sync.WaitGroup) {
			lock.Lock()
			i++
			sum += i
			lock.Unlock()

			wg.Done()
		}(&lock, &wg)
	}

	wg.Wait()

	return sum
}
